from typing import Tuple, Optional, Callable

import fitz  # PyMuPDF

from app.utils.pymupdf_tools import pdf_get_content_region, detect_page_number_region as pdf_get_page_num, _get_pages_to_process, crop_pdf_with_redactions, crop_pdf_with_white_mask
from tests.base_test import base_test_case

logger = base_test_case.get_logger(__name__)
TEST_DATA_DIR = base_test_case.test_data_dir
OUTPUT_DATA_DIR = base_test_case.output_data_dir


# # 三个方案的对比总结
# # 方案 函数名 数据是否被删除 文件大小 可逆性 内容可提取性 (PyMuPDF) 适用场景
# # :--- :--- :--- :--- :--- :--- :---
# # 1. 编辑删除 crop_pdf_with_redactions 是 (永久) 可能减小 ❌ ❌ 需要彻底移除敏感信息，分发最终版
# # 2. 白色遮罩 crop_pdf_with_white_mask 否 (被覆盖) 可能增大 ❌ ✅ 视觉上隐藏内容，但仍需程序访问原始数据
# # 3. 裁剪框 crop_pdf_with_cropbox 否 (被隐藏) 基本不变 ✅ ✅ 创建可逆的“工作视图”，保持完全可编辑性


# --- 修改后的裁剪函数 ---
# 它们现在接受一个 `roi_detector` 函数，该函数负责为每页生成 ROI


def crop_pdf_with_cropbox(
        input_path: str,
        output_path: str,
        roi_detector: Callable[[fitz.Page], Optional[Tuple[int, int, int, int]]],
        page_range=None,
        zero_based=True,
        detector_kwargs: dict = None
):
    """
    方案3: 使用 set_cropbox，ROI 动态提供，并确保其在 MediaBox 内x
    效果: 仅修改视觉裁剪框。原始数据完全保留，可逆。

    Args:
        input_path (str): 输入 PDF 路径。
        output_path (str): 输出 PDF 路径。
        roi_detector: ROI 区域 动态提供。
        page_range (int, list, None): 要处理的页码 (同上)。
        zero_based (bool): 页码是否为 0-based。
        detector_kwargs:
    """
    detector_kwargs = detector_kwargs or {}
    try:
        doc = fitz.open(input_path)
        total_pages = doc.page_count

        pages_to_process = _get_pages_to_process(page_range, total_pages, zero_based)

        for page_index in pages_to_process:
            page = doc[page_index]
            roi = roi_detector(page, **detector_kwargs)
            if roi is None:
                print(f"[方案3] 第 {page_index + 1} 页: ROI 检测失败，跳过。")
                continue

            # --- 关键修复: 约束 ROI 在 MediaBox 内 ---
            media_box = page.rect  # 获取页面的 MediaBox
            x0, y0, x1, y1 = roi

            # 确保 x0 >= media_box.x0
            x0 = max(x0, media_box.x0)
            # 确保 y0 >= media_box.y0
            y0 = max(y0, media_box.y0)
            # 确保 x1 <= media_box.x1
            x1 = min(x1, media_box.x1)
            # 确保 y1 <= media_box.y1
            y1 = min(y1, media_box.y1)

            # 检查约束后的矩形是否有效 (面积 > 0)
            if x0 >= x1 or y0 >= y1:
                print(f"[方案3] 第 {page_index + 1} 页: 约束后 ROI 无效 (x0={x0}, y0={y0}, x1={x1}, y1={y1})，跳过。")
                continue

            constrained_roi = (x0, y0, x1, y1)
            # set_cropbox 是“隐藏”，而不是“删除”
            page.set_cropbox(fitz.Rect(constrained_roi))
            print(f"[方案3] 已设置第 {page_index + 1} 页的 CropBox: {constrained_roi}")

        doc.save(output_path)
        doc.close()
        print(f"[方案3] 已保存: {output_path}")

    except Exception as e:
        print(f"[方案3] 处理失败: {e}")
        if 'doc' in locals():
            doc.close()



def crop_pdf_physically(
        input_path: str,
        output_path: str,
        roi_detector: Callable[[fitz.Page], Optional[Tuple[int, int, int, int]]],
        page_range=None,
        zero_based=True,
        detector_kwargs: dict = None
):
    """
    方案4: 物理裁剪。创建一个全新的、尺寸等于 ROI 的 PDF。
    效果: 永久移除 ROI 外部的内容，文件可能更小。
    """
    detector_kwargs = detector_kwargs or {}
    try:
        doc_in = fitz.open(input_path)
        doc_out = fitz.open()  # 创建一个全新的空 PDF
        total_pages = doc_in.page_count

        pages_to_process = _get_pages_to_process(page_range, total_pages, zero_based)

        for page_index in pages_to_process:
            src_page = doc_in[page_index]
            roi = roi_detector(src_page, **detector_kwargs)
            if roi is None:
                print(f"[方案4] 第 {page_index + 1} 页: ROI 检测失败，跳过。")
                continue

            media_box = src_page.rect
            x0, y0, x1, y1 = roi

            # 约束 ROI 在 MediaBox 内
            x0 = max(x0, media_box.x0)
            y0 = max(y0, media_box.y0)
            x1 = min(x1, media_box.x1)
            y1 = min(y1, media_box.y1)

            if x0 >= x1 or y0 >= y1:
                print(f"[方案4] 第 {page_index + 1} 页: 约束后 ROI 无效，跳过。")
                continue

            # --- 核心区别: 创建一个新页面 ---
            new_rect = fitz.Rect(x0, y0, x1, y1)
            # 将源页面的内容复制到一个新页面，该新页面的尺寸正好是 ROI
            new_page = doc_out.new_page(width=new_rect.width, height=new_rect.height)

            # 使用 show_pdf_page 将源页面的指定区域渲染到新页面
            new_page.show_pdf_page(new_page.rect, doc_in, page_index, clip=fitz.Rect(x0, y0, x1, y1))

            print(f"[方案4] 已创建第 {page_index + 1} 页，尺寸: {new_rect}")

        doc_out.save(output_path)
        doc_out.close()
        doc_in.close()
        print(f"[方案4] 已保存: {output_path}")

    except Exception as e:
        print(f"[方案4] 处理失败: {e}")
        if 'doc_out' in locals():
            doc_out.close()
        if 'doc_in' in locals():
            doc_in.close()


def crop_pdf_keep_roi(
        input_path: str,
        output_path: str,
        roi_detector: Callable[[fitz.Page], Optional[Tuple[int, int, int, int]]],
        page_range=None,
        zero_based=True,
        detector_kwargs: dict = None,
        fill_color: tuple = (1, 1, 1)  # 白色 (RGB)
):
    """
    方案6 (修正版): 保留 ROI 区域，删除外部内容，并保持原页面尺寸。
    效果: 新的 PDF 页面尺寸与原页面相同，ROI 外部被填充为指定颜色（默认白色），只有 ROI 区域可见。

    Args:
        input_path: 输入 PDF 路径
        output_path: 输出 PDF 路径
        roi_detector: ROI 检测函数，返回 (x0, y0, x1, y1)
        page_range: 要处理的页码
        zero_based: 页码是否从0开始
        detector_kwargs: 传递给检测器的参数
        fill_color: 用于填充外部区域的颜色，默认为白色 (1,1,1)
    """
    detector_kwargs = detector_kwargs or {}
    try:
        doc = fitz.open(input_path)
        total_pages = doc.page_count

        pages_to_process = _get_pages_to_process(page_range, total_pages, zero_based)

        for page_index in pages_to_process:
            page = doc[page_index]
            roi = roi_detector(page, **detector_kwargs)
            if roi is None:
                print(f"[方案6] 第 {page_index + 1} 页: ROI 检测失败，跳过。")
                continue

            media_box = page.rect
            x0, y0, x1, y1 = roi

            # 约束 ROI 在 MediaBox 内
            x0 = max(x0, media_box.x0)
            y0 = max(y0, media_box.y0)
            x1 = min(x1, media_box.x1)
            y1 = min(y1, media_box.y1)

            if x0 >= x1 or y0 >= y1:
                print(f"[方案6] 第 {page_index + 1} 页: 约束后 ROI 无效，跳过。")
                continue

            # --- 核心逻辑: 在 ROI 外部绘制白色矩形 ---
            # 定义 ROI 矩形
            roi_rect = fitz.Rect(x0, y0, x1, y1)

            # 1. 覆盖左侧条带
            if x0 > media_box.x0:
                left_strip = fitz.Rect(media_box.x0, media_box.y0, x0, media_box.y1)
                page.draw_rect(left_strip, color=fill_color, fill=fill_color, overlay=True)

            # 2. 覆盖右侧条带
            if x1 < media_box.x1:
                right_strip = fitz.Rect(x1, media_box.y0, media_box.x1, media_box.y1)
                page.draw_rect(right_strip, color=fill_color, fill=fill_color, overlay=True)

            # 3. 覆盖顶部条带
            if y0 > media_box.y0:
                top_strip = fitz.Rect(x0, media_box.y0, x1, y0)
                page.draw_rect(top_strip, color=fill_color, fill=fill_color, overlay=True)

            # 4. 覆盖底部条带
            if y1 < media_box.y1:
                bottom_strip = fitz.Rect(x0, y1, x1, media_box.y1)
                page.draw_rect(bottom_strip, color=fill_color, fill=fill_color, overlay=True)

            # ✅ 可选: 设置 CropBox 以提示阅读器关注 ROI 区域
            # page.set_cropbox(roi_rect)

            print(f"[方案6] 已处理第 {page_index + 1} 页: 保留 ROI ({x0}, {y0}, {x1}, {y1})，外部已填充")

        # 保存文档
        doc.save(output_path)
        doc.close()
        print(f"[方案6] 已保存: {output_path}")

    except Exception as e:
        print(f"[方案6] 处理失败: {e}")
        import traceback
        traceback.print_exc()
        if 'doc' in locals():
            doc.close()


if __name__ == "__main__":
    # ------------------- 使用示例 -------------------
    input_pdf = TEST_DATA_DIR / "1711605374231.pdf"
    output_prefix = "1711605374231"


    # 定义检测器函数（带参数）
    def my_roi_detector(page):
        return pdf_get_content_region(page, zoom_factor=1.0, is_show_log=False)


    # 执行个方案
    # crop_pdf_with_redactions(input_pdf, OUTPUT_DATA_DIR / f"{output_prefix}_redactions.pdf", my_roi_detector, page_range=None)
    crop_pdf_with_redactions(input_pdf, OUTPUT_DATA_DIR / f"{output_prefix}_redactions.pdf", my_roi_detector, page_range=[0, 1])
    crop_pdf_with_white_mask(input_pdf, OUTPUT_DATA_DIR / f"{output_prefix}_mask.pdf", my_roi_detector, page_range=[0, 1])
    crop_pdf_with_cropbox(input_pdf, OUTPUT_DATA_DIR / f"{output_prefix}_cropbox.pdf", my_roi_detector, page_range=[0, 1])
    # 方案4
    crop_pdf_physically(input_pdf, OUTPUT_DATA_DIR / f"{output_prefix}_crop_pdf_physically.pdf", my_roi_detector, page_range=[0, 1])
    # 方案6
    crop_pdf_keep_roi(input_pdf, OUTPUT_DATA_DIR / f"{output_prefix}_crop_pdf_keep_roi.pdf", my_roi_detector, page_range=[0, 1])

    # 添加遮罩层后,可以获取页码
    pdf_get_page_num(OUTPUT_DATA_DIR / f"{output_prefix}_mask.pdf", page_range=[0, 1, 3,4])
    pdf_get_page_num(OUTPUT_DATA_DIR / f"{output_prefix}_crop_pdf_keep_roi.pdf", page_range=[0, 1, 3,4])
    # 物理裁切,不可获取页码
    # pdf_get_page_num(OUTPUT_DATA_DIR / f"{output_prefix}_crop_pdf_physically.pdf", page_range=[0, 1, 3,4])
    # pdf_get_page_num(OUTPUT_DATA_DIR / f"{output_prefix}_cropbox.pdf", page_range=[0, 1, 3, 4])
    # pdf_get_page_num(OUTPUT_DATA_DIR / f"{output_prefix}_redactions.pdf", page_range=[0, 1, 3,4])

    print("所有方案执行完毕。")

    # 定义一个带有自定义参数的 roi_detector
    def my_roi_detector(page, zoom_factor=1.0, top_margin=60, bottom_margin=35, left_right_margin=(50, 150)):
        return pdf_get_content_region(
            page,
            zoom_factor=zoom_factor,
            is_show_log=False,
            top_margin=top_margin,
            bottom_margin=bottom_margin,
            left_right_margin=left_right_margin
        )


    # # 使用不同的参数调用裁剪函数
    # crop_pdf_with_cropbox(input_pdf, OUTPUT_DATA_DIR / f"{output_prefix}_cropbox.pdf", my_roi_detector, page_numbers=[2], detector_kwargs={
    #     'zoom_factor': 1.5,
    #     'top_margin': 80,  # 更大的顶部边距
    #     'bottom_margin': 45,  # 更大的底部边距
    #     'left_right_margin': (70, 170)  # 扩大左右扫描范围
    # })

    # try:
    #     with fitz.open(OUTPUT_DATA_DIR / f"{output_prefix}_redactions.pdf") as doc:
    #         total = doc.page_count
    #         print(f"📄 共 {total} 页\n")
    #
    #         raw_numbers = []
    #         for i in range(total):
    #             page = doc[i]
    #             # 读取页码
    #             pdf_get_page_num(page)
    #             page_num = pdf_get_content_region(page, zoom_factor=1.0, top_margin=60, bottom_margin=35, left_right_margin=(50, 150))
    #
    #             print("原始识别结果:", page_num)
    #
    #
    # except Exception as e:
    #     print(f"❌ 处理失败: {e}")
    # finally:
    #     doc.close()
